#pragma once
#include <iostream>
#include <sys/epoll.h>
#include <cerrno>
#include <cstring>
#include "nocopy.hpp"

#include "Log.hpp"

class Epoller : public nocopy // 禁止拷贝
{
    static const int size = 128; // epoll_create 的参数
public:
    Epoller() 
    {
        // 创建 epoll 句柄
        _epfd = epoll_create(size);
        if (_epfd == -1)
        {
            lg(Error, "epoll_create error: %s", strerror(errno));
        }
        else
        {
            lg(Info, "epoll_craete success: %d", _epfd);
        }
    }

    // 封装 epoll_wait 函数
    int EpollerWait(struct epoll_event revents[], int num, int timeout = -1)
    {
        int n = epoll_wait(_epfd, revents, num, timeout);
        return n;
    }

    // 封装 epoll_ctl 函数
    int EpollerUpdate(int oper, int sock, uint32_t event)
    {
        int n = 0;
        if (oper == EPOLL_CTL_DEL) // -> 从 epoll 的监视列表中删除一个文件描述符(即参数 fd)
        {
            std::cout << "EPOLL_CTL_DEL" << std::endl;
            n = epoll_ctl(_epfd, oper, sock, nullptr);
            if (n != 0)
            {
                lg(Error, "epoll_ctl delete error!");
            }
            else lg(Info, "epoll_ctl delete success!");
        }
        else 
        {
            // EPOLL_CTL_MOD -> 修改监视列表中已经存在的描述符(即参数 fd)对应的监视事件类型(参数 event)
            // or 
            // EPOLL_CTL_ADD -> 在 epoll 的监视列表中添加一个文件描述符(即参数 fd), 并指定监视的事件类型(参数 event)
            struct epoll_event ev;
            ev.events = event;
            ev.data.fd = sock; // 方便我们以后得知是哪一个 fd 就绪了

            n = epoll_ctl(_epfd, oper, sock, &ev);
            if (n != 0)
            {
                lg(Error, "epoll_ctl error! ");
            }
            if (oper == EPOLL_CTL_ADD) lg(Info, "epoll_ctl add success!");
            else lg(Info, "epoll_ctl mod success!");
        }
        return n;
    }

    ~Epoller() 
    {
        if (_epfd >= 0)
        {
            close(_epfd);
        }
    }
private:
    int _epfd;
    int _timeout{3000};
};